#include "worklistwidget.h"
#include "ui_worklistwidget.h"

#include <QSortFilterProxyModel>
#include <QMessageBox>
#include <QSettings>
#include <QDate>
#include <QCompleter>
#include <QSqlQuery>
#include <QSqlError>
#include <QSqlQueryModel>
#include <QTimer>
#include <QSqlDatabase>
#include <QJsonObject>
#include <QDir>

#include "worklistitemmodel.h"
#include "patientselectdialog.h"
#include "wlistscuthread.h"
#include "../../ImageAcquisitor/ImageAcquisitor/global.h"

#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dcuid.h"

#define WLIST_AUTOUPDATE_CHECKED "WLISTAUTOUPDATECHECKED"
#define WLIST_AUTOUPDATE_INTERVAL "WLISTAUTOUPDATEINTERVAL"

WorklistWidget::WorklistWidget(QWidget *parent) :
    m_proc(0),
    m_userData(0),
    QWidget(parent),
    ui(new Ui::WorklistWidget)
{
    ui->setupUi(this);
    init();
}

WorklistWidget::~WorklistWidget()
{
    QSettings s;
    s.setValue(WLIST_AUTOUPDATE_CHECKED, ui->autoUpdateCheck->isChecked());
    s.setValue(WLIST_AUTOUPDATE_INTERVAL, ui->autoUpdateSpin->value());

    delete ui;
}

void WorklistWidget::init()
{
    setupComponents();
    setupConnections();
    initDatabase();
    loadWlistScps();
    on_resetButton_clicked();
    on_todayButton_clicked();
}

void WorklistWidget::initDatabase()
{
    if (QSqlDatabase::database().isOpen())
        return;

    QString err;
    QSettings s(LOCAL_NETWORK_CONFIG, QSettings::IniFormat);
    s.setIniCodec(QSETTINGS_CODEC_NAME);
    QString t = s.value(NETWORK_DBMS_TYPE).toString();
    QSqlDatabase db = QSqlDatabase::addDatabase(t);
    if (db.isValid()) {
        QString h = s.value(NETWORK_DBMS_HOST).toString();
        int p = s.value(NETWORK_DBMS_PORT).toInt();
        QString u = s.value(NETWORK_DBMS_USER).toString();
        QString pwd = s.value(NETWORK_DBMS_PWD).toString();
        QString d = s.value(NETWORK_DBMS_NAME).toString();
        db.setDatabaseName(d);
        db.setHostName(h);
        db.setPort(p);
        db.setUserName(u);
        db.setPassword(pwd);
        if (db.open()) {

        } else {
            err = tr("Connect to database failed.");
        }
    } else {
        err = tr("Invalid database type: ") + t;
    }

    if ((!err.isEmpty()) && m_proc) {
        QJsonObject json;
        json["action"] = "ShowMessage";
        json["args"] = err;
        m_proc(json, m_userData);
    }
}

void WorklistWidget::loadWlistScps()
{
    QDir dir(DICOM_WLISTSCP_DIR);
    QStringList subs = dir.entryList(QStringList()<<"*.ini", QDir::Files);
    foreach (const QString &s, subs) {
        ui->serverCombo->addItem(s.left(s.lastIndexOf(".")));
    }
    ui->autoUpdateCheck->setEnabled(subs.size());
}

void WorklistWidget::setupComponents()
{
    timer = new QTimer(this);
    pModel = new QSqlQueryModel(this);
    completer = new QCompleter(this);
    completer->setModel(pModel);
    completer->setModelSorting(QCompleter::CaseSensitivelySortedModel);
    ui->newPatientNameEdit->setCompleter(completer);

    wlistModel = new WorklistItemModel(this);
    wlistProxyModel = new QSortFilterProxyModel(this);
    wlistProxyModel->setSourceModel(wlistModel);
    ui->wlistTableView->setModel(wlistProxyModel);
    ui->wlistTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    wlistThread = new WlistSCUThread(wlistModel, this);
}

void WorklistWidget::on_resetButton_clicked()
{
    QSettings s(LOCAL_IDFORMAT_CONFIG, QSettings::IniFormat);
    s.setIniCodec(QSETTINGS_CODEC_NAME);
    int start = s.value(IDFORMAT_PATIENTID_START).toInt();
    int digits = s.value(IDFORMAT_PATIENTID_DIGITS).toInt();
    QString prefix = s.value(IDFORMAT_PATIENTID_PREFIX).toString();
    QString suffix = s.value(IDFORMAT_PATIENTID_SUFFIX).toString();
    ui->newPatientIdEdit->setText(QString("%1%2%3").arg(prefix)
                               .arg(start, digits, 10, QChar('0'))
                               .arg(suffix));
    start = s.value(IDFORMAT_ACCNUM_START).toInt();
    digits = s.value(IDFORMAT_ACCNUM_DIGITS).toInt();
    prefix = s.value(IDFORMAT_ACCNUM_PREFIX).toString();
    suffix = s.value(IDFORMAT_ACCNUM_SUFFIX).toString();
    ui->newAccNumEdit->setText(QString("%1%2%3").arg(prefix)
                            .arg(start, digits, 10, QChar('0'))
                            .arg(suffix));

    QString query = "SELECT DISTINCT pati_name FROM study_t ORDER BY pati_name";
    pModel->setQuery(query, QSqlDatabase::database());

    ui->autoUpdateSpin->setValue(s.value(WLIST_AUTOUPDATE_INTERVAL, 30).toInt());
    ui->autoUpdateCheck->setChecked(s.value(WLIST_AUTOUPDATE_CHECKED, false).toBool());
    on_autoUpdateSpin_valueChanged(ui->autoUpdateSpin->value());

    ui->newPatientBirthDateEdit->setMaximumDate(QDate::currentDate());
    ui->newPatientBirthDateEdit->setDate(QDate::currentDate());
    ui->newPatientAgeSpin->setValue(0);
    ui->newPatientNameEdit->clear();
    ui->newMedicalAlertEdit->clear();

    enableComponents(true);
}

void WorklistWidget::enableComponents(bool yes)
{
    ui->newPatientIdEdit->setEnabled(yes);
    ui->newPatientNameEdit->setEnabled(yes);
    ui->newPatientSexCombo->setEnabled(yes);
    ui->newPatientBirthDateEdit->setEnabled(yes);
    ui->newPatientAgeCombo->setEnabled(yes);
    ui->newPatientAgeSpin->setEnabled(yes);
}

void WorklistWidget::setupConnections()
{
    connect(completer, SIGNAL(activated(QString)), this, SLOT(patientSelected(QString)));
    connect(ui->newPatientAgeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateBirth()));
    connect(ui->newPatientAgeSpin, SIGNAL(valueChanged(int)), this, SLOT(updateBirth()));
    connect(ui->newPatientBirthDateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(updateAge(QDate)));
    connect(wlistThread, SIGNAL(finished()), this, SLOT(onWlistThreadFinished()));
    connect(timer, SIGNAL(timeout()), this, SLOT(on_searchButton_clicked()));
}

void WorklistWidget::patientSelected(const QString &name)
{
    PatientSelectDialog dialog(name, this);
    if (QDialog::Accepted == dialog.exec()) {
        const QSqlRecord &study= dialog.getStudy();
        ui->newPatientIdEdit->setText(study.value("pati_id").toString());
        ui->newPatientNameEdit->setText(study.value("pati_name").toString());
        ui->newPatientSexCombo->setCurrentText(sex2trSex(study.value("pati_sex").toString()));
        ui->newPatientBirthDateEdit->setDate(study.value("pati_birth").toDate());
        ui->newAdmissionIdEdit->setText(study.value("pati_adid").toString());
        ui->newMedicalAlertEdit->setText(study.value("pati_alert").toString());
        updateAge(ui->newPatientBirthDateEdit->date(), true);
        enableComponents(false);
    }
}

void WorklistWidget::updateAge(const QDate &date, bool enforce)
{
    if (enforce || ui->newPatientBirthDateEdit->hasFocus()) {
        QDate curDate = QDate::currentDate();
        if (curDate.year() - date.year() > 1) {
            ui->newPatientAgeSpin->setValue(curDate.year()-date.year());
            ui->newPatientAgeCombo->setCurrentIndex(0);
        } else if ((curDate.year() > date.year()) || (curDate.month() - date.month() > 1)) {
            ui->newPatientAgeSpin->setValue(curDate.month()-date.month()+(curDate.year()-date.year())*12);
            ui->newPatientAgeCombo->setCurrentIndex(1);
        } else if ((curDate.month() > date.month()) || (curDate.weekNumber()-date.weekNumber()>1)) {
            ui->newPatientAgeSpin->setValue(date.daysTo(curDate)/7);
            ui->newPatientAgeCombo->setCurrentIndex(2);
        } else {
            ui->newPatientAgeSpin->setValue(date.daysTo(curDate));
            ui->newPatientAgeCombo->setCurrentIndex(3);
        }
    }
}

void WorklistWidget::updateBirth()
{
    if (ui->newPatientAgeCombo->hasFocus() || ui->newPatientAgeSpin->hasFocus()) {
        int value = ui->newPatientAgeSpin->value();
        QDate curDate = QDate::currentDate();
        ui->newPatientBirthDateEdit->setMaximumDate(curDate);
        switch (ui->newPatientAgeCombo->currentIndex()) {
        case 0:
            ui->newPatientBirthDateEdit->setDate(curDate.addYears(-value));
            break;
        case 1:
            ui->newPatientBirthDateEdit->setDate(curDate.addMonths(-value));
            break;
        case 2:
            ui->newPatientBirthDateEdit->setDate(curDate.addDays(-(value*7)));
            break;
        case 3:
            ui->newPatientBirthDateEdit->setDate(curDate.addDays(-value));
            break;
        }
    }
}

bool WorklistWidget::insertStudyToDB(const WorklistItem &item)
{
    QString msg;
    QSqlQuery q(QSqlDatabase::database());
    q.prepare("INSERT INTO study_t(study_iuid, pati_id, pati_name, pati_sex, pati_birth, pati_age, pati_adid, pati_alert, acc_num, study_time, modality, insti_id_fk, task_phy_id_fk, stat_id_fk, study_desc)"
              " VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
    q.addBindValue(item.studyUid);
    q.addBindValue(item.patientId);
    q.addBindValue(item.patientName);
    q.addBindValue(item.patientSex);
    q.addBindValue(item.patientBirth);
    q.addBindValue(item.patientAge);
    q.addBindValue(item.admissionId);
    q.addBindValue(item.medicalAlert);
    //q.addBindValue(item.reqPhysician);
    q.addBindValue(item.accNumber);
    q.addBindValue(QDateTime::currentDateTime()/*.toString(DATETIME_FORMAT)*/);
    q.addBindValue(item.schModality);
    q.addBindValue("10001");
    q.addBindValue("30001");
    q.addBindValue("0001");
    q.addBindValue(item.studyDesc);
    bool ret;
    if (ret = q.exec()) {
        msg = tr("Insert study to database ok.");
    } else {
        msg = tr("Insert study to database failed: ") + q.lastError().text();
    }
    if (m_proc) {
        QJsonObject json;
        json["action"] = "ShowMessage";
        json["args"] = msg;
        m_proc(json, m_userData);
    }
    return ret;
}

void WorklistWidget::on_wlistTableView_activated(const QModelIndex &index)
{
    QModelIndex idx = wlistProxyModel->mapToSource(index);
    WorklistItem *item = static_cast<WorklistItem*>(idx.internalPointer());
    if (item) {
        if (item->studyUid.isEmpty()) {
            char uid[128];
            dcmGenerateUniqueIdentifier(uid, SITE_STUDY_UID_ROOT);
            item->studyUid = QString::fromLatin1(uid);
        }
        if (insertStudyToDB(*item) && m_proc) {
            QJsonObject json;
            json["action"] = "StartStudy";
            json["args"] = item->studyUid;
            m_proc(json, m_userData);
        }
    }
}

void WorklistWidget::on_startAcqBtn_clicked()
{
    if (ui->newAccNumEdit->text().isEmpty() || ui->newPatientIdEdit->text().isEmpty() ||
            ui->newPatientNameEdit->text().isEmpty() || (ui->newPatientAgeSpin->value()==0)) {
        QMessageBox::critical(this, tr("Register Study"), tr("Mandatory fields empty."));
        return;
    }

    WorklistItem item;
    char uid[128];
    dcmGenerateUniqueIdentifier(uid, SITE_STUDY_UID_ROOT);
    item.studyUid = QString::fromLatin1(uid);
    item.accNumber = ui->newAccNumEdit->text();
    item.patientId = ui->newPatientIdEdit->text();
    item.patientName = ui->newPatientNameEdit->text();
    item.patientSex = trSex2Sex(ui->newPatientSexCombo->currentText());
    item.patientBirth = ui->newPatientBirthDateEdit->date();

    QString ageUnit;
    switch (ui->newPatientAgeCombo->currentIndex()) {
    case 0:
        ageUnit = "Y";
        break;
    case 1:
        ageUnit = "M";
        break;
    case 2:
        ageUnit = "W";
        break;
    case 3:
        ageUnit = "D";
        break;
    }
    item.patientAge = ui->newPatientAgeSpin->text() + ageUnit;

    item.medicalAlert = ui->newMedicalAlertEdit->text();
    item.admissionId = ui->newAdmissionIdEdit->text();
    item.schModality = ui->newModalityCombo->currentText();
    item.reqPhysician = ui->newReqPhysicianCombo->currentText();
    item.studyDesc = ui->newStudyDescEdit->text();

    if (insertStudyToDB(item)) {
        QSettings s(LOCAL_IDFORMAT_CONFIG, QSettings::IniFormat);
        s.setIniCodec(QSETTINGS_CODEC_NAME);
        if (ui->newPatientIdEdit->isEnabled())
            s.setValue(IDFORMAT_PATIENTID_START, s.value(IDFORMAT_PATIENTID_START).toInt()+1);
        s.setValue(IDFORMAT_ACCNUM_START, s.value(IDFORMAT_ACCNUM_START).toInt()+1);
        if (m_proc) {
            QJsonObject json;
            json["action"] = "StartStudy";
            json["args"] = item.studyUid;
            m_proc(json, m_userData);
        }
    }
}

void WorklistWidget::showEvent(QShowEvent *e)
{
    on_resetButton_clicked();
    ui->patientNameEdit->setFocus(Qt::MouseFocusReason);
    QWidget::showEvent(e);
}

void WorklistWidget::on_todayButton_clicked()
{
    ui->fromCheck->setChecked(true);
    ui->toCheck->setChecked(true);
    ui->fromDateTimeEdit->setDate(QDate::currentDate());
    ui->fromDateTimeEdit->setTime(QTime(0, 0));
    ui->toDateTimeEdit->setDate(QDate::currentDate());
    ui->toDateTimeEdit->setTime(QTime(23, 59, 59, 999));
    emit ui->searchButton->clicked(true);
}

void WorklistWidget::on_thisWeekButton_clicked()
{
    ui->fromCheck->setChecked(true);
    ui->toCheck->setChecked(true);
    ui->fromDateTimeEdit->setDate(QDate::currentDate());
    ui->fromDateTimeEdit->setTime(QTime(0, 0));
    ui->toDateTimeEdit->setDate(QDate::currentDate().addDays(6));
    ui->toDateTimeEdit->setTime(QTime(23, 59, 59, 999));
    emit ui->searchButton->clicked(true);
}

void WorklistWidget::on_thisMonthButton_clicked()
{
    ui->fromCheck->setChecked(true);
    ui->toCheck->setChecked(true);
    ui->fromDateTimeEdit->setDate(QDate::currentDate());
    ui->fromDateTimeEdit->setTime(QTime(0, 0));
    ui->toDateTimeEdit->setDate(QDate::currentDate().addDays(30));
    ui->toDateTimeEdit->setTime(QTime(23, 59, 59, 999));
    emit ui->searchButton->clicked(true);
}

void WorklistWidget::on_autoUpdateCheck_toggled(bool checked)
{
    if (ui->autoUpdateCheck->isEnabled()) {
        if (checked) timer->start();
        else timer->stop();
    } else {
        ui->autoUpdateCheck->setChecked(false);
    }
}

void WorklistWidget::on_autoUpdateSpin_valueChanged(int value)
{
    timer->setInterval(value*1000);
}

void WorklistWidget::on_searchButton_clicked(bool checked)
{
    if (checked) {
        if (wlistThread->isRunning()) {
            wlistThread->setAbort(true);
            wlistThread->wait(3000);
        }
        wlistThread->setWlistScp(ui->serverCombo->currentText()+".ini");
        wlistThread->setAccNumber(ui->accNumberEdit->text());
        wlistThread->setPatientId(ui->patientIdEdit->text());
        wlistThread->setPatientName(ui->patientNameEdit->text());
        wlistThread->setFromTime(ui->fromCheck->isChecked()?
                                    ui->fromDateTimeEdit->dateTime():
                                    QDateTime());
        wlistThread->setToTime(ui->toCheck->isChecked()?
                                  ui->toDateTimeEdit->dateTime():
                                  QDateTime());
        wlistThread->setModality(ui->modalityCombo->currentText());
        wlistModel->clearAllItems();
        ui->searchButton->setText(tr("Abort"));
        wlistThread->start();
    } else {
        wlistThread->setAbort(true);
    }
}

void WorklistWidget::onWlistThreadFinished()
{
    ui->searchButton->setChecked(false);
    ui->searchButton->setText(tr("Search"));
}

void WorklistWidget::on_clearButton_clicked()
{
    ui->patientIdEdit->clear();
    ui->accNumberEdit->clear();
    ui->patientNameEdit->clear();
    ui->modalityCombo->setCurrentIndex(0);
    ui->fromCheck->setChecked(false);
    ui->toCheck->setChecked(false);
}
